package org.boofcv.video.slice;

import boofcv.abst.filter.derivative.ImageGradient;
import boofcv.factory.filter.derivative.FactoryDerivative;
import boofcv.ohos.VisualizeImageData;
import boofcv.ohos.camera.VisualizeCameraSlice;
import boofcv.struct.image.GrayS16;
import boofcv.struct.image.GrayU8;
import boofcv.struct.image.ImageBase;
import boofcv.struct.image.ImageType;
import ohos.aafwk.content.Intent;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.StackLayout;
import ohos.agp.components.surfaceprovider.SurfaceProvider;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.TextAlignment;
import ohos.media.camera.device.Camera;
import ohos.media.camera.device.CameraAbility;
import ohos.media.camera.device.FrameConfig;
import ohos.media.camera.params.Metadata;
import ohos.media.image.PixelMap;
import org.boofcv.video.ResourceTable;

import java.util.Locale;

public class GradientSlice extends VisualizeCameraSlice {
    // Storage for the gradient
    private GrayS16 derivX = new GrayS16(1, 1);
    private GrayS16 derivY = new GrayS16(1, 1);

    // Storage for image gradient. In general you will want to precompute data structures due
    // to the expense of garbage collection
    private ImageGradient<GrayU8, GrayS16> gradient = FactoryDerivative.three(GrayU8.class, GrayS16.class);

    // Used to display text info on the display
    private Paint paintText = new Paint();

    public GradientSlice() {
        // The default behavior for selecting the camera's resolution is to
        // find the resolution which comes the closest to having this many
        // pixels.
        targetResolution = 640 * 480;
    }

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_slice_gradient);

//        // Java 1.8 issues with older SDK versions

        StackLayout layout = (StackLayout) findComponentById(ResourceTable.Id_sl_camera_frame);

        // By calling this function you are telling the camera library that you wish to process
        // images in a gray scale format. The video stream is typically in YUV420. Color
        // image formats are supported as RGB, YUV, ... etc, color spaces.
        setImageType(ImageType.single(GrayU8.class));

        // Configure paint used to display FPS
        paintText.setStrokeWidth(4 * AttrHelper.getDensity(this));
        paintText.setTextSize((int) (14 * AttrHelper.getDensity(this)));
        paintText.setTextAlign(TextAlignment.LEFT);
        paintText.setColor(new Color(Color.argb(0xFF, 0xFF, 0xB0, 0)));
//        paintText.setTypeface(Typeface.create(Typeface.MONOSPACE, Typeface.BOLD));

        // The camera stream will now start after this function is called.
        startCamera(layout);
    }

    /**
     * This is where you specify custom camera settings. See {@link boofcv.ohos.camera.SimpleCameraSlice}'s
     * JavaDoc for more functions which you can override.
     *
     * @param device The camera being configured
     * @param characteristics Used to get information on the device
     * @param captureRequestBuilder used to configure the camera
     */
    @Override
    protected void configureCamera(Camera device, CameraAbility characteristics, FrameConfig.Builder captureRequestBuilder) {
        captureRequestBuilder.setAfMode(Metadata.AfMode.AF_MODE_CONTINUOUS, null);
        captureRequestBuilder.setAeMode(Metadata.AeMode.AE_MODE_ON, null);
    }

    /**
     * During camera initialization this function is called once after the resolution is known.
     * This is a good function to override and predeclare data structres which are dependent
     * on the video feeds resolution.
     *
     * @param width
     * @param height
     * @param sensorOrientation
     */
    @Override
    protected void onCameraResolutionChange(int width, int height, int sensorOrientation) {
        super.onCameraResolutionChange(width, height, sensorOrientation);

        derivX.reshape(width, height);
        derivY.reshape(width, height);
    }

    /**
     * This function is invoked in its own thread and can take as long as you want.
     *
     * @param image The image which is to be processed. The image is owned by this function until
     * it returns. After that the image and all it's data will be recycled. DO NOT
     */
    @Override
    protected void processImage(ImageBase image) {
        // The data type of 'image' was specified in onCreate() function
        // The line below will compute the gradient and store it in two images. One for the
        // gradient along the x-axis and the other along the y-axis
        gradient.process((GrayU8) image, derivX, derivY);
    }

    /**
     * Override the default behavior and colorize gradient instead of converting input image.
     *
     * @param mode
     * @param image
     */
    @Override
    protected void renderBitmapImage(BitmapMode mode, ImageBase image) {
        switch (mode) {
            case UNSAFE: { // this application is configured to use double buffer and could ignore all other modes
                VisualizeImageData.colorizeGradient(derivX, derivY, -1, bitmap, bitmapTmp);
            }
            break;

            case DOUBLE_BUFFER: {
                VisualizeImageData.colorizeGradient(derivX, derivY, -1, bitmapWork, bitmapTmp);

                if (bitmapLock.tryLock()) {
                    try {
                        PixelMap tmp = bitmapWork;
                        bitmapWork = bitmap;
                        bitmap = tmp;
                    } finally {
                        bitmapLock.unlock();
                    }
                }
            }
            break;
        }
    }

    /**
     * Demonstrates how to draw visuals
     *
     * @param view
     * @param canvas
     */
    @Override
    protected void onDrawFrame(SurfaceProvider view, Canvas canvas) {
        super.onDrawFrame(view, canvas);

        // Display info on the image being process and how fast input camera
        // stream (probably in YUV420) is converted into a BoofCV format
        resetCanvas(canvas);
        int width = bitmap.getImageInfo().size.width;
        int height = bitmap.getImageInfo().size.height;
        canvas.drawText(paintText, String.format(Locale.getDefault(),
                "%d x %d Convert: %4.1f (ms)",
                width, height, periodConvert.getAverage()), 40, 120);
        // Pro tip: Run in app fast or release mode for a dramatic speed up!
        // In DevEco Studio expand "Build Variants" tab on left.
    }
}
